package com.task.sql.schedule.worker;/**
 * @ClassName : ExecuteTaskWorker
 * @Description : 待执行任务执行器
 * @Author : liudonghai
 * @Date: 2024-09-11 16:38
 */

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.task.sql.schedule.constant.DBColumnValue;
import com.task.sql.schedule.constant.DBOperaterValue;
import com.task.sql.schedule.domain.entity.SqlContent;
import com.task.sql.schedule.domain.entity.SqlTaskSchedule;
import com.task.sql.schedule.service.ExecutorCustomSQLService;
import com.task.sql.schedule.service.SqlContentService;
import com.task.sql.schedule.service.SqlTaskScheduleService;
import com.task.sql.schedule.service.SqlTaskService;
import com.task.sql.schedule.util.db.SqlTypeSwitch;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.sql.SQLException;
import java.sql.Wrapper;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName : ExecuteTaskWorker
 * @Description : 待执行任务执行器
 * @Author : liudonghai
 * @Date: 2024-09-11 16:38
 */
@Slf4j
@Component
public class ExecuteTaskWorker {
    @Autowired
    Environment environment;
    @Value("${worker.failed.max_num:0}")
    private int failedMaxNum;
    @Resource
    private SqlTaskScheduleService sqlTaskScheduleService;
    @Resource
    private SqlTaskService sqlTaskService;
    @Resource
    private SqlContentService sqlContentService;
    @Resource
    private ExecutorCustomSQLService executorCustomSQLService;


    @Scheduled(cron="${worker.execute.cron}")
    public void task(){
        System.out.println("------------execute start-----------");
        List<SqlTaskSchedule> sqlTaskSchedules = sqlTaskScheduleService.listPendingTasks(failedMaxNum);
        if(CollectionUtil.isEmpty(sqlTaskSchedules)){
            log.info("未获取到任何需要执行处理的任务数据");
            return;
        }
        for (SqlTaskSchedule task:sqlTaskSchedules) {
            //锁定任务
            boolean locked = lockScheduleTask(task);
            if(!locked){
                log.info(String.format("任务%s锁定失败",task.getId()));
                continue;
            }
            if(isPreTaskExec(task)){
                SqlContent one = sqlContentService.query().eq("task_no", task.getTaskNo()).eq("yn", DBColumnValue.YN_1.getCode()).one();

                System.out.println(one.getContent());
                boolean res = Boolean.TRUE;
                try {
                    res = executorCustomSQLService.executorSQLInTransaction(one.getContent());
                } catch (Exception e) {
                    res = Boolean.FALSE;
                    log.info("Executor SQL in Traction Failed!!");
                    e.printStackTrace();
                    //throw new RuntimeException(e);
                }
                boolean dealRes = res ? dealScheduleResultSuccess(task) : dealScheduleResultFailed(task);
            }
        }
        System.out.println("------------execute end-----------");
    }

    /**
     * 判断是否有前置依赖任务未执行
     * 1.0.0版本不支持该功能
     * @param task
     * @return 该任务以及任务链的所有前置任务都执行完成，返回true；
     *         有任何一个前置任务未执行或执行失败，返回false
     */
    private boolean isPreTaskExec(SqlTaskSchedule task){
        return true;
    }

    private boolean lockScheduleTask(SqlTaskSchedule task){
        int num = task.getNum().intValue();
        int state = task.getExecState().intValue();
        int failedNum = task.getFailedNum().intValue();
        if(failedNum>=failedMaxNum){
            task.setFailedNum(0);
        }
        task.setNum(num+1);
        task.setExecState(DBColumnValue.EXEC_STATE_1.getCode());
        task.setExecAddr(environment.getProperty("local.server.address"));
        task.setExecDesc(DBColumnValue.EXEC_STATE_1.getMsg());
        task.setExecHandler(Thread.currentThread().getId()+"");
        UpdateWrapper<SqlTaskSchedule> wrapper = new UpdateWrapper<SqlTaskSchedule>();
        wrapper.eq("id", task.getId()).eq("num", num).eq("exec_state", state);
        boolean res = sqlTaskScheduleService.update(task, wrapper);
        return res;
    }

    private boolean dealScheduleResultSuccess(SqlTaskSchedule task){
        int num = task.getNum().intValue();
        int state = task.getExecState().intValue();
        task.setNum(num+1);
        task.setExecState(DBColumnValue.EXEC_STATE_2.getCode());
        task.setExecDesc(DBColumnValue.EXEC_STATE_2.getMsg());
        UpdateWrapper<SqlTaskSchedule> wrapper = new UpdateWrapper<SqlTaskSchedule>();
        wrapper.eq("id", task.getId()).eq("num", num).eq("exec_state", state);
        boolean res = sqlTaskScheduleService.update(task, wrapper);
        return res;
    }
    private boolean dealScheduleResultFailed(SqlTaskSchedule task){
        int failedNum = task.getFailedNum().intValue();
        int state = task.getExecState().intValue();
        task.setFailedNum(failedNum+1);
        task.setExecState(DBColumnValue.EXEC_STATE_3.getCode());
        task.setExecDesc(DBColumnValue.EXEC_STATE_3.getMsg());
        UpdateWrapper<SqlTaskSchedule> wrapper = new UpdateWrapper<SqlTaskSchedule>();
        wrapper.eq("id", task.getId()).eq("failed_num", failedNum).eq("exec_state", state);
        boolean res = sqlTaskScheduleService.update(task, wrapper);
        return res;
    }
}
